package com.rabbit.handler;

import cn.dev33.satoken.exception.NotLoginException;
import cn.dev33.satoken.exception.NotPermissionException;
import cn.dev33.satoken.exception.NotRoleException;
import com.rabbit.constant.ResultCodeEnum;
import com.rabbit.exception.BusinessException;
import com.rabbit.util.ResultGenUtil;
import com.rabbit.vo.ResultVO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.jdbc.BadSqlGrammarException;
import org.springframework.stereotype.Component;
import org.springframework.validation.BindException;
import org.springframework.validation.BindingResult;
import org.springframework.web.HttpMediaTypeNotSupportedException;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.NoHandlerFoundException;

import java.lang.reflect.UndeclaredThrowableException;
import java.nio.file.AccessDeniedException;
import java.sql.SQLException;
import java.util.Objects;

/**
 * @author zjw
 * @date 2019/3/7
 */
@RestControllerAdvice
@Component
@Slf4j
public class GlobalExceptionHandler {

    /**
     * IllegalArgumentException异常处理返回json
     * 返回状态码:400
     */
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(IllegalArgumentException.class)
    public ResultVO<Object> badRequestException(IllegalArgumentException e) {
        log.error("参数解析失败:", e);
        return ResultGenUtil.genFail(ResultCodeEnum.ILLEGAL_ARGUMENT_EXCEPTION, e);
    }

    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(BindException.class)
    public ResultVO<Object> bindExceptionException(BindException e) {
        log.error("参数解析失败:", e);
        return ResultGenUtil.genFail(ResultCodeEnum.ILLEGAL_ARGUMENT_EXCEPTION, e.getFieldError().getDefaultMessage());
    }


    /**
     * AccessDeniedException异常处理返回json
     * 返回状态码:403
     */
    @ResponseStatus(HttpStatus.FORBIDDEN)
    @ExceptionHandler(AccessDeniedException.class)
    public ResultVO<Object> badMethodExpressException(AccessDeniedException e) {
        log.error("没有权限请求当前方法:", e);
        return ResultGenUtil.genFail(ResultCodeEnum.ACCESS_DENIED_EXCEPTION, e);
    }

    /**
     * 不支持当前请求方法
     * 返回状态码:405
     */
    @ResponseStatus(HttpStatus.METHOD_NOT_ALLOWED)
    @ExceptionHandler(HttpRequestMethodNotSupportedException.class)
    public ResultVO<Object> handleHttpRequestMethodNotSupportedException(HttpRequestMethodNotSupportedException e) {
        return ResultGenUtil.genFail(ResultCodeEnum.METHOD_NOT_ALLOWED_EXCEPTION, e);
    }

    /**
     * 不支持的媒体类型
     * 返回状态码:415
     */
    @ResponseStatus(HttpStatus.UNSUPPORTED_MEDIA_TYPE)
    @ExceptionHandler(HttpMediaTypeNotSupportedException.class)
    public ResultVO<Object> handleHttpMediaTypeNotSupportedException(HttpMediaTypeNotSupportedException e) {
        return ResultGenUtil.genFail(ResultCodeEnum.HTTP_MEDIA_TYPE_NOT_SUPPORTED_EXCEPTION, e);
    }

    /**
     * SQLException sql异常处理
     * 返回状态码:500
     */
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    @ExceptionHandler({SQLException.class, BadSqlGrammarException.class})
    public ResultVO<Object> handleSQLException(SQLException e) {
        log.error("服务运行SQLException异常", e);
        return ResultGenUtil.genFail(ResultCodeEnum.SQL_EXCEPTION, e);
    }

    /***
     * 10005 - 必输项不能为空
     */
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public ResultVO<Object> handleMethodArgumentNotValidException(MethodArgumentNotValidException e) {
        BindingResult bindingResult = e.getBindingResult();
        return ResultGenUtil.genCustom(ResultCodeEnum.ARGUMENTS_NOT_VALID_ERROR.getCode(),
                bindingResult.getAllErrors().get(0).getDefaultMessage());
    }

    /**
     * 找不到请求路径对应的处理
     * 返回状态码:500
     */
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    @ExceptionHandler(NoHandlerFoundException.class)
    public ResultVO<Object> handleNoHandlerFoundException(SQLException e) {
        log.error("找不到请求路径对应的处理", e);
        return ResultGenUtil.genFail(ResultCodeEnum.NO_HANDLER_FOUND, e);
    }

    /**
     * 未登录相关异常
     */
    @ResponseStatus(HttpStatus.FORBIDDEN)
    @ExceptionHandler(NotLoginException.class)
    public ResultVO<Object> handleNotLoginException(NotLoginException e) {
        log.error("未登录异常:{}", e.getMessage());
        if (Objects.equals(e.getType(), NotLoginException.NOT_TOKEN)) {
            return ResultGenUtil.genFail(ResultCodeEnum.TOKEN_EMPTY);
        } else if (Objects.equals(e.getType(), NotLoginException.INVALID_TOKEN)) {
            return ResultGenUtil.genFail(ResultCodeEnum.TOKEN_ILLEGAL);
        } else if (Objects.equals(e.getType(), NotLoginException.TOKEN_TIMEOUT)) {
            return ResultGenUtil.genFail(ResultCodeEnum.TOKEN_EXPIRE);
        } else if (Objects.equals(e.getType(), NotLoginException.KICK_OUT)) {
            return ResultGenUtil.genFail(ResultCodeEnum.TOKEN_KICK_OUT_ERROR);
        }
        return ResultGenUtil.genFail(ResultCodeEnum.TOKEN_ILLEGAL);
    }

    /**
     * 权限不足异常
     */
    @ResponseStatus(HttpStatus.FORBIDDEN)
    @ExceptionHandler(NotRoleException.class)
    public ResultVO<Object> handleNotRoleException(NotRoleException e) {
        log.error("当前用户角色无权操作异常:{}", e.getMessage());
        return ResultGenUtil.genFail(ResultCodeEnum.TOKEN_ROLE_ERROR);
    }

    /**
     * 权限不足异常
     */
    @ResponseStatus(HttpStatus.FORBIDDEN)
    @ExceptionHandler(NotPermissionException.class)
    public ResultVO<Object> handleNotPermissionException(NotPermissionException e) {
        log.error("当前角色匹配，但权限不足异常:{}", e.getMessage());
        return ResultGenUtil.genFail(ResultCodeEnum.TOKEN_PERMISSION_ERROR);
    }

    /**
     * 业务处理异常
     */
    @ExceptionHandler(BusinessException.class)
    public ResultVO<Object> handleBusinessException(BusinessException e) {
        if (e.getResultCodeEnum() == null) {
            log.error("捕获到业务错误,具体描述:{}", e.getMessage());
            return ResultGenUtil.genCustom(e.getCode(), e.getMessage());
        } else {
            log.error("捕获到业务错误,具体描述:{}", e.getResultCodeEnum().getMessage());
            return ResultGenUtil.genFail(e.getResultCodeEnum());
        }
    }

    /**
     * 切面中抛出异常
     * 返回状态码:500
     */
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    @ExceptionHandler(UndeclaredThrowableException.class)
    public ResultVO<Object> handleUndeclaredThrowableException(UndeclaredThrowableException e) {
        if (e.getUndeclaredThrowable() instanceof BusinessException) {
            BusinessException exception = (BusinessException) e.getUndeclaredThrowable();
            return ResultGenUtil.genFail(exception.getResultCodeEnum());
        } else {
            return ResultGenUtil.genFail(ResultCodeEnum.UNKNOWN_ERROR);
        }
    }

    /**
     * 其余异常统一处理
     * 返回状态码:500
     */
    @ExceptionHandler(Exception.class)
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    public ResultVO<Object> handleException(Exception e) {
        log.error("遇到未捕获错误，具体错误原因:", e);
        return ResultGenUtil.genFail(ResultCodeEnum.UNKNOWN_ERROR);
    }
}